package example.utils;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class DistributedLockHandler {
    Logger logger = LoggerFactory.getLogger(DistributedLockHandler.class);
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 守护线程 运行状态
     */
    private volatile boolean loopFlag = false;

    /**
     * 获取锁失败重试次数
     */
    private int retryNum = 5;

    /**
     * 获取锁
     *
     * @param lockName 锁名称
     * @param value    锁value
     * @param expire   锁的过期时间
     * @param millis   获取锁失败后，重试获取锁的时间间隔（单位，毫秒）----例如 3000 = 3秒
     * @return
     */
    public boolean lock(String lockName, String value, long expire, TimeUnit unit, long millis) {
        boolean result = onLock(lockName, value, expire, unit);
        // 获取系统时间：毫秒
        long beginTime = System.currentTimeMillis();
        // 没有获取到锁 && 没有超过重试次数 && 在重试时间的区间内
        while ((!result) && (retryNum-- > 0) && ((System.currentTimeMillis() - beginTime) < millis)) {
            try {
                result = onLock(lockName, value, expire, unit);
            } catch (Exception e) {
                log.info("Exception:", e);
            }
        }
        if (!result) {
            this.loopFlag = false;
        }
        log.info("获取锁成功： lockName:{},  value:{},  expire:{},  unit:{},  millis:{}", lockName, value, expire, unit, millis);
        return result;
    }

    /**
     * @param lockName 锁名称
     * @param value    锁value
     * @param expire   锁的过期时间
     * @return
     */
    public boolean onLock(String lockName, String value, long expire, TimeUnit unit) {
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(lockName, value, expire, unit);
        if (aBoolean) {
            this.loopFlag = true;
            new Thread(new ExtendCycle(lockName, expire, unit)).start();
            return true;
        }
        return false;
    }

    /**
     * 将自己的锁释放掉
     *
     * @param key
     * @param value
     * @return
     */
    public boolean releaseLock(String key, String value) {
        String lockValue = (String) redisTemplate.opsForValue().get(key);
        // 判断是否是自己的锁
        if (lockValue.equals(value)) {
            this.loopFlag = false;
            log.info("释放锁：key:{}  value:{}  this.loopFlag: {}", key, value, this.loopFlag);
            Boolean delete = redisTemplate.delete(key);
            return true;
        }
        return false;
    }

    /**
     * 守护线程--更改给定多的过期时间
     */
    class ExtendCycle implements Runnable {
        private String lockName;
        private long expire;
        private TimeUnit unit;

        public ExtendCycle(String lockName, long expire, TimeUnit unit) {
            this.lockName = lockName;
            this.expire = expire;
            this.unit = unit;
        }

        @Override
        public void run() {
            long begin = System.currentTimeMillis();
            long reference = expire * 2 / 3;
            try {
                // 当前时间距离上次设置的时间超过 设定过期时间 的 三分之一后开始重新设置
                while (loopFlag && ((System.currentTimeMillis() - begin) >= reference)) {
                    redisTemplate.expire(lockName, expire, unit);
                    begin = System.currentTimeMillis();
                    log.info("延长过期时间： ockName:{}, expire{}, unit{}, 睡眠时间：{}", lockName, expire, unit, (expire * 1000 * 2 / 3));
                }
            } catch (Exception e) {
                log.info("Exception:", e);
            }
        }

    }

}
